
#include <BFile>
#include <BReadWrite>
#include <BApplication>
#include "IHtmlStyleReader.h"
#include "member_BHtmlWidget.h"
#include "member_BHtmlItem.h"
#include "member_BXmlNode.h"
#include "member_BStyle.h"
#include "member_BTexture.h"

using namespace BWE;

member_BHtmlWidget::member_BHtmlWidget(BHtmlWidget* htmlWidget)
{
	boss = htmlWidget;
	blinkTick = 0.0;
	blinkTime = 1.0;
	editable = false;
	blinkShown = false;
	autoTitle = true;
	selectMode = Select_Single;

	dirty = true;
	dirtyHead = true;
	dirtyStyle = true;
	dirtyResource = true;
	editing = false;

	hoveredItem = 0;
	hoverIndex = 0;
	pressed = false;
	pressedItem = 0;
	pressedIndex = 0;
	htmlStyle = new IHtmlStyle();

}
member_BHtmlWidget::~member_BHtmlWidget()
{

}

void member_BHtmlWidget::initStyle()
{
	const BStyle* style = boss->realStyle();
	const BString& family = BApplication::font()->family();
	htmlStyle->clearValues();
	htmlStyle->clearSubs();
	htmlStyle->clearChildren();
	htmlStyle->setValue("font", new BFont(family));
	htmlStyle->setValue("color", style->color(Color_Text, State_Normal));

	if (IHtmlStyle* linkStyle = htmlStyle->addChild("a"))
	{
		linkStyle->setValue("color", BColor(26, 80, 128));
	}

	if (IHtmlStyle* paragraphStyle = htmlStyle->addChild("p"))
	{
		paragraphStyle->setValue("margin-top", 10);
		paragraphStyle->setValue("margin-bottom", 10);
	}

	if (IHtmlStyle* divStyle = htmlStyle->addChild("div"))
	{
		divStyle->setValue("margin-top", 10);
		divStyle->setValue("margin-bottom", 10);
	}

	if (IHtmlStyle* tableStyle = htmlStyle->addChild("table"))
	{
		tableStyle->setValue("margin-top", 10);
		tableStyle->setValue("margin-bottom", 10);
	}

	if (IHtmlStyle* listStyle = htmlStyle->addChild("ul"))
	{
		listStyle->setValue("padding-left", 10);
		listStyle->setValue("padding-right", 10);
		listStyle->setValue("margin-top", 10);
		listStyle->setValue("margin-bottom", 10);
	}
	if (IHtmlStyle* listStyle = htmlStyle->addChild("ol"))
	{
		listStyle->setValue("padding-left", 10);
		listStyle->setValue("padding-right", 10);
		listStyle->setValue("margin-top", 10);
		listStyle->setValue("margin-bottom", 10);
	}
	if (IHtmlStyle* listStyle = htmlStyle->addChild("dl"))
	{
		listStyle->setValue("padding-left", 10);
		listStyle->setValue("padding-right", 10);
		listStyle->setValue("margin-top", 10);
		listStyle->setValue("margin-bottom", 10);
	}

	if (IHtmlStyle* spanStyle = htmlStyle->addChild("span"))
	{
		spanStyle->setValue("margin-left", 10);
		spanStyle->setValue("margin-right", 10);
		spanStyle->setValue("margin-top", 10);
		spanStyle->setValue("margin-bottom", 10);
	}

	BString name;
	for (int i = 0; i < 8; i++)
	{
		name.reset();
		name << 'h' << i;
		IHtmlStyle* headStyle = htmlStyle->addChild(name);
		headStyle->setValue("margin-top", 10);
		headStyle->setValue("margin-bottom", 10);
	}
	htmlStyle->child("h1")->setValue("font", new BFont(family, 36));
	htmlStyle->child("h2")->setValue("font", new BFont(family, 30));
	htmlStyle->child("h3")->setValue("font", new BFont(family, 24));
	htmlStyle->child("h4")->setValue("font", new BFont(family, 18));
	htmlStyle->child("h5")->setValue("font", new BFont(family, 12));

	htmlStyle->addChild("sub")->setValue("font-size", 0.72f);
	htmlStyle->addChild("sup")->setValue("font-size", 0.72f);

}
void member_BHtmlWidget::initHead()
{
	if (headNode)
	{
		if (autoTitle)
		{
			if (BXmlNode* titleNode = headNode->child("title"))
			{
				boss->setTitle(titleNode->text());
			}
		}
		if (BXmlNode* linkNode = headNode->child("link"))
		{
			if (linkNode->attrib("rel") == "stylesheet" && linkNode->attrib("type") == "text/css")
			{
				const BString& href = linkNode->attrib("href");
				if (BFile::Exist(href))
				{
					IHtmlStyleReader reader;
					reader.load(href, htmlStyle);
					printf("HTML load style sheet %s", href.cstr());
					if (reader.message().size())
						printf(" message :\n%s\n", reader.message().cstr());
					else
						printf(" success.\n");
				}
				else
				{
					printf("HTML load style sheet %s failed : file is not exist.\n", href.cstr());
				}
			}
		}
		if (BXmlNode* styleNode = headNode->child("style"))
		{
			const BString& text = styleNode->text();
			if (text.size())
			{
				IHtmlStyleReader reader;
				reader.read(text, htmlStyle);
				printf("HTML read style sheet");
				if (reader.message().size())
					printf(" message :\n%s\n", reader.message().cstr());
				else
					printf(" success.\n");
			}
		}
	}
}
void member_BHtmlWidget::loadResource(BHtmlItem* item)
{
	if (!item)
		return;

	const BString& name = item->name();
	if (name == "img" && item_member(item)->image.empty())
	{
		const BString& src = item->attrib("src");
		if (BImage* image = ReadFile<BImage>(src))
		{
			item_member(item)->image = image;
			texture_member(image)->wrap = BTexture::Wrap_Repeat;
			texture_member(image)->filter = BTexture::Filter_Nearest;
		}
	}

	for (int i = 0; i < item->childCount(); i++)
	{
		BHtmlItem* child = item->child(i);
		loadResource(child);
	}
}
void member_BHtmlWidget::freshStyle()
{
	floatItems.reset();
	fontStack.reset();
	fontStack.push(boss->realFont());
	if (bodyItem)
	{
		freshStyle(bodyItem);
	}
	fontStack.pop();
}
void member_BHtmlWidget::freshStyle(BHtmlItem* item)
{
	if (item_member(item)->dirtyStyle)
	{
		item_member(item)->html = boss;
		item_member(item)->resetStyle();
		htmlStyle->apply(item);
		if (BHtmlItem* parent = item->parent())
			dispenseUpperStyle(item, item->parent());
		item_member(item)->applyStyle(fontStack.last());
		if (item_member(item)->floatAlign)
			floatItems.append(item);
		item_member(item)->dirtyStyle = false;
	}
	fontStack.push(item_member(item)->realFont);
	for (int i = 0; i < item->childCount(); i++)
	{
		BHtmlItem* child = item->child(i);
		freshStyle(child);
	}
	fontStack.pop();
}
void member_BHtmlWidget::dispenseUpperStyle(BHtmlItem* item, BHtmlItem* upper)
{
	if (BHtmlItem* parent = upper->parent())
	{
		const BString& name = upper->name();
		dispenseUpperStyle(item, parent);
	}
	const BString& name = upper->name();
	if (IHtmlStyle* htmlStyle = item_member(upper)->realStyle)
	{
		htmlStyle->apply(item);
	}
}
void member_BHtmlWidget::freshPageArea()
{
	BSize bodySize;
	if (bodyItem)
	{
		int page = 0;
		if (boss->areaPolicy().widthPolicy() != Policy_Preferred)
			page = boss->centerSize().width();
		bodySize = item_member(bodyItem)->freshBlock(0, page);
	}
	boss->setAreaSize(bodySize);
}
BLine2 member_BHtmlWidget::inputLine()
{
	BLine2 line;
	if (dirty)
	{
		dirty = false;
		freshPageArea();
	}
	if (BHtmlItem* item = selectedItems.first())
	{
		const BPoint& offset = boss->offset();
		member_BHtmlItem* itemem = item_member(item);
		if (itemem->cells.empty())
		{
			BRect rect = itemem->textRect + offset;
			line.set(rect.leftTop(), rect.leftBottom());
		}
		else if (itemem->cells.check(itemem->inputIndex))
		{
			ICodeCell& cell = itemem->cells[itemem->inputIndex];
			BRect rect = cell.rect + offset;
			line.set(rect.leftTop(), rect.leftBottom());
		}
		else
		{
			ICodeCell& cell = itemem->cells.last();
			BRect rect = cell.rect + offset;
			line.set(rect.rightTop(), rect.rightBottom());
		}
	}
	return line;
}

void member_BHtmlWidget::paintItem(BHtmlItem* item, BPainter& painter)
{
	if (item_member(item)->floatAlign)
		return;

	BPoint offset = boss->area().pos();
	BRect rect = item_member(item)->rect + offset;

	if (!painter.clip().intersect(rect))
		return;

	item_member(item)->paint(painter, offset);

	BArray<BXmlNodeHolder>& children = xml_member(item)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* child = children[i].as<BHtmlItem>();
		paintItem(child, painter);
	}
}
void member_BHtmlWidget::paintFloatItem(BHtmlItem* item, BPainter& painter)
{
	BPoint offset = boss->area().pos();
	BRect rect = item_member(item)->rect + offset;

	if (!painter.clip().intersect(rect))
		return;

	item_member(item)->paint(painter, offset);

	BArray<BXmlNodeHolder>& children = xml_member(item)->children;
	for (int i = 0; i < children.size(); i++)
	{
		BHtmlItem* child = children[i].as<BHtmlItem>();
		paintFloatItem(child, painter);
	}
}

bool member_BHtmlWidget::selectItems()
{
	if (!pressedItem || !hoveredItem)
		return false;
	if (pressedItem == hoveredItem)
	{
		item_member(pressedItem)->inputCount = abs(pressedIndex - hoverIndex);
		item_member(pressedItem)->inputIndex = bMin(pressedIndex, hoverIndex);
	}
	else
	{

	}
	return true;
}
bool member_BHtmlWidget::removeSelection()
{
	if (BHtmlItem* focusedItem = selectedItems.first())
	{
		if (item_member(focusedItem)->removeSelection())
		{
			editing = true;
			dirty = true;
			return true;
		}
	}
	return false;
}

void member_BHtmlWidget::itemTextEdited(BHtmlItem* item)
{
	dirty = true;
	editing = true;
	blinkShown = true;
	blinkTick += blinkTime;
	boss->emit(Signal_ItemChanged, item);
	boss->emit(Signal_ItemEdited, item);
	boss->fresh();
}

BHtmlItem* member_BHtmlWidget::findItem(const BPoint& pos)
{
	for (int i = 0; i < floatItems.size(); i++)
	{
		BHtmlItem* item = floatItems[i];
		BHtmlItem* result = findFloatItem(pos, item);
		if (result)
			return result;
	}
	if (bodyItem)
		return findItem(pos, bodyItem);
	return 0;
}
BHtmlItem* member_BHtmlWidget::findItem(const BPoint& pos, BHtmlItem* item)
{
	if (floatItems.contain(item))
		return 0;
	for (int i = 0; i < item_member(item)->cells.size(); i++)
	{
		ICodeCell& cell = item_member(item)->cells[i];
		BRect rect = cell.rect;
		if (cell.rect.contain(pos))
			return item;
	}
	if (item_member(item)->display == BHtmlItem::Display_Inline)
	{
		for (int i = 0; i < item->childCount(); i++)
		{
			BHtmlItem* child = item->child(i);
			BHtmlItem* ret = findItem(pos, child);
			if (ret)
				return ret;
		}
		if (editable)
		{
			const BRect& area = item_member(item)->area;
			if (area.contain(pos))
				return item;
		}
		return 0;
	}
	BRect rect = item_member(item)->rect;
	if (rect.contain(pos))
	{
		for (int i = 0; i < item->childCount(); i++)
		{
			BHtmlItem* child = item->child(i);
			BHtmlItem* ret = findItem(pos, child);
			if (ret)
				return ret;
		}
		return item;
	}
	return 0;
}
BHtmlItem* member_BHtmlWidget::findFloatItem(const BPoint& pos, BHtmlItem* item)
{
	if (item_member(item)->display == BHtmlItem::Display_Inline)
	{
		for (int i = 0; i < item->childCount(); i++)
		{
			BHtmlItem* child = item->child(i);
			BHtmlItem* ret = findFloatItem(pos, child);
			if (ret)
				return ret;
		}
		for (int i = 0; i < item_member(item)->cells.size(); i++)
		{
			ICodeCell& cell = item_member(item)->cells[i];
			BRect rect = cell.rect;
			if (cell.rect.contain(pos))
				return item;
		}
		return 0;
	}
	BRect rect = item_member(item)->rect;
	if (rect.contain(pos))
	{
		for (int i = 0; i < item->childCount(); i++)
		{
			BHtmlItem* child = item->child(i);
			BHtmlItem* ret = findFloatItem(pos, child);
			if (ret)
				return ret;
		}
		return item;
	}
	return 0;
}
BHtmlItem* member_BHtmlWidget::itemOfId(BHtmlItem* item, const BString& id)
{
	if (item->attrib("id") == id)
		return item;
	for (int i = 0; i < item->childCount(); i++)
	{
		BHtmlItem* child = item->child(i);
		BHtmlItem* ret = itemOfId(child, id);
		if (ret)
			return ret;
	}
	return 0;
}

void member_BHtmlWidget::slotHeadChanged(BObject* object, const BValue& value)
{
	dirty = true;
	dirtyHead = true;
	dirtyStyle = true;
	dirtyResource = true;
	boss->fresh();
}
void member_BHtmlWidget::slotBodyChanged(BObject* object, const BValue& value)
{
	boss->emit(Signal_ItemChanged, object);
}
